﻿using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using PgBirdie.Core.Extensions;
using PgBirdie.Core.Helpers;
using PgBirdie.Core.Model;

namespace PgBirdie.Core.Config
{
    public sealed class Settings
    {
        private static bool IsPopulated { get; set; }

        public static ConfigForRun Run { get; set; }
        public static ConfigForPaths PathTo { get; set; }
        public static ConfigForMisc Misc { get; set; }
        public static ConfigForSplitters SplitterFor { get; set; }
        public static ConfigTarget Target { get; set; }
        public static ConfigSource Source { get; set; }

        /// <summary>
        /// Initialize constant settings.
        /// </summary>
        static Settings()
        {
            Misc = new ConfigForMisc();
            SplitterFor = new ConfigForSplitters();
        }

        /// <summary>
        /// Initialize per-run settings from xml-based configuration file.
        /// </summary>
        public static ExitCodeType PopulateFromConfigFile(CommandType commandType, string pathToConfigFile)
        {
            if (IsPopulated)
                throw new Exception("Settings from xml have already been set.");

            var document = new XDocument();
            try
            {
                document = XDocument.Load(pathToConfigFile);
            }
            catch (FileNotFoundException)
            {
                AppConsole.WriteLine(String.Format("Settings file does not exist at {0}. Please check the main config file.",
                                                   String.IsNullOrEmpty(pathToConfigFile) ? "<no value given>" : pathToConfigFile));
                return ExitCodeType.Failure;
            }
            catch (UnauthorizedAccessException)
            {
                AppConsole.WriteLine(String.Format("Access to given path {0} was denied. Please check the path. It should point to a xml configuration file.",
                                                   String.IsNullOrEmpty(pathToConfigFile) ? "<no value given>" : pathToConfigFile));
                return ExitCodeType.Failure;
            }

            var run = new ConfigForRun();
            run.CommandType = commandType;
            Run = run;

            var pathTo = new ConfigForPaths();
            pathTo.PSql = document.Root.Element("paths").Element("psql").AsString();
            pathTo.PgDump = document.Root.Element("paths").Element("pgdump").AsString();
            pathTo.Dump = document.Root.Element("paths").Element("dump").AsString();
            pathTo.Delta = document.Root.Element("paths").Element("delta").AsString();
            PathTo = pathTo;

            var target = new ConfigTarget();
            target.Host = document.Root.Element("target").Attribute("host").AsString();
            target.Port = document.Root.Element("target").Attribute("port").AsString();
            target.Role = document.Root.Element("target").Attribute("role").AsString();
            target.Password = document.Root.Element("target").Attribute("password").AsString();
            target.Database = document.Root.Element("target").Attribute("database").AsString();
            Target = target;

            var source = new ConfigSource();
            source.Host = document.Root.Element("source").Attribute("host").AsString();
            source.Port = document.Root.Element("source").Attribute("port").AsString();
            source.Role = document.Root.Element("source").Attribute("role").AsString();
            source.Password = document.Root.Element("source").Attribute("password").AsString();
            source.Database = document.Root.Element("source").Attribute("database").AsString();
            Source = source;

            var schemas = from x in document.Root.Element("source").Element("schemas").Descendants("schema") select x;

            foreach (var schema in schemas)
            {
                var s = new ConfigSchema();
                s.Name = schema.Attribute("name").AsString();

                foreach (var sequence in schema.Element("sequences").ToIEnumberable())
                {
                    var seq = new ConfigSequence();
                    seq.Name = sequence.Attribute("name").AsString();
                    seq.Schema = s;
                    s.Sequences.Add(seq.Name, seq);
                }

                foreach (var table in schema.Element("tables").ToIEnumberable())
                {
                    var t = new ConfigTable();
                    t.Name = table.Attribute("name").AsString();
                    t.WithData = table.Attribute("withData").ToBool();
                    var excludeColumns = table.Attribute("exclude").AsString();
                    if (!String.IsNullOrEmpty(excludeColumns))
                        t.ExcludeStaticDataColumnNames = excludeColumns.Split('|');

                    t.Schema = s;
                    s.Tables.Add(t.Name, t);
                }

                Source.Schemas.Add(s.Name, s);
            }

            IsPopulated = true;
            return ExitCodeType.Success;
        }
    }
}